/*
 * Dummy file to start a Chisel project.
 *
 * Author: Martin Schoeberl (martin@jopdesign.com)
 * 
 */

package MYMAC

import chisel3._
import chisel3.util._

class MACIO(dataWidth:Int) extends Bundle {
  //inputs
  val dataIn = Input(SInt(dataWidth.W))//The 8-bit activation value to multiply by weight.
  val accIn = Input(SInt(32.W))//32-bit value to accumulate with product.
  val switchw = Input(Bool())//Control signal; when 1, switch to using the other weight buffer.
  val weightIn = Input(SInt(8.W))//8-bit value to write to the secondary weight buffer.
  val weightWe = Input(Bool())//When high, weights are being written; if tag matches, store weights. Otherwise, pass them through with incremented tag.
  val weightTag = Input(UInt(8.W))

  //output
  val outDelay = Output(SInt())//Result of the multiply accumulate; moves one cell down to become acc_in
  val dataDelay = Output(SInt())//data_in, stored in a pipeline register for cell to the right
  val switchDelay = Output(Bool())//switchw, stored in a pipeline register for cell to the right
  val weightDelay = Output(SInt())//weight_in, stored in a pipeline register for cell below
  val weightWeDelay = Output(Bool())//weight_we, stored in a pipeline register for cell below
  val weightTagDelay = Output(UInt())//weight_tag, incremented and stored in a pipeline register for cell below
}

object MYMAC {
  private var id = 0

  def getID() = {
    id += 1
    id
  }
}

class MYMAC(matrixSize:Int, dataWidth:Int) extends Module {
  val io = IO(new Bundle{
    val macIO = new MACIO(dataWidth)
  })

  val wbuf1, wbuf2 = RegInit(0.S(dataWidth.W))
  val currentBufferReg = RegInit(0.U(1.W))
  when(io.macIO.switchw){
    currentBufferReg := ~currentBufferReg
  }

  val currenBuffer = currentBufferReg ^ io.macIO.switchw

  when(io.macIO.weightWe && io.macIO.weightTag===(matrixSize-1).U){
    when(currenBuffer===0.U){
      wbuf2 := io.macIO.weightIn
    }.otherwise{
      wbuf1 := io.macIO.weightIn
    }
  }

  val weight = Mux(currenBuffer===0.U, wbuf2, wbuf1)
  val product = weight * io.macIO.dataIn
  val out = product + io.macIO.accIn

  //println(" weight, io.macIO.dataIn,io.macIO.accIn,out \n", weight.toInt , io.macIO.dataIn.toInt,io.macIO.accIn.toInt,out.toInt);

  io.macIO.dataDelay := RegNext(io.macIO.dataIn, 0.S)
  io.macIO.switchDelay := RegNext(io.macIO.switchw, 0.U)
  io.macIO.outDelay := RegNext(out, 0.S)
  io.macIO.weightDelay := RegNext(io.macIO.weightIn, 0.S)
  io.macIO.weightWeDelay := RegNext(io.macIO.weightWe, 0.U)
  io.macIO.weightTagDelay := RegNext((io.macIO.weightTag + 1.U), 0.U)
}

// object MACMain extends App {
//   println("Generating the MAC hardware")
//   emitVerilog(new MYMAC(3, 8), Array("--target-dir", "generated"))
// }
